package com.wx.pay.service.impl;

import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.wx.pay.common.ResultData;
import com.wx.pay.config.WXConfig;
import com.wx.pay.service.WXPayService;
import com.wx.pay.utils.WeiXinPayUtil;
import com.wx.pay.vo.PayVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 朝花不迟暮
 * @version 1.0
 * @date 2021/2/10 15:46
 */
@Service
public class WXPayServiceImpl implements WXPayService
{
    private static final Logger LOGGER = LoggerFactory.getLogger(WXPayServiceImpl.class);

    @Value("${wxapplet.config.weixinpay.notifyurl}")
    public String notifyurl;
    //public static final String NOTIFY_URL = "http://feb9434a2fb8.ngrok.io/payback/wxappletpaycallback"; //内网穿透  80端口才行http://feb9434a2fb8.ngrok.io
    public static final String TRADE_TYPE_JSAPI = "JSAPI";

    @Override
    public ResultData doUnifiedOrder(PayVO payVO)
    {
        WXConfig config = null;
        WXPay wxpay = null;
        try
        {
            config = new WXConfig();
            wxpay = new WXPay(config);
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        //生成的随机字符串
        String nonce_str = WXPayUtil.generateNonceStr();
        //获取客户端的ip地址
        //获取本机的ip地址
        InetAddress addr = null;
        try
        {
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e)
        {
            e.printStackTrace();
        }
        String spbill_create_ip = addr.getHostAddress();
        //支付金额，需要转成字符串类型，否则后面的签名会失败 测试就直接定义死了，根据自己的业务修改
        String totalFee = "1";
        //商品描述
        String body = "金币充值";
        //商户订单号
        String out_trade_no = WXPayUtil.generateNonceStr();
        //统一下单接口参数
        HashMap<String, String> data = new HashMap<String, String>();
        try
        {
            data.put("appid", config.getAppID());
            data.put("body", body);
            data.put("mch_id", config.getMchID());
            data.put("nonce_str", nonce_str);
            data.put("notify_url", notifyurl);
            data.put("openid", payVO.getOpenid());
            data.put("out_trade_no", out_trade_no);
            data.put("spbill_create_ip", spbill_create_ip);
            data.put("total_fee", totalFee);
            data.put("trade_type", TRADE_TYPE_JSAPI);
            data.put("sign", WXPayUtil.generateSignature(data, config.getKey(), WXPayConstants.SignType.MD5));
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        try
        {
            Map<String, String> rMap = wxpay.unifiedOrder(data);
            //System.out.println("统一下单接口返回: " + rMap);
            LOGGER.error("统一下单接口返回:[{}] ", rMap);
            String return_code = (String) rMap.get("return_code");
            String result_code = (String) rMap.get("result_code");
            String nonceStr = WXPayUtil.generateNonceStr();
            Map resultMap = new ConcurrentHashMap();
            resultMap.put("nonceStr", nonceStr);
            Long timeStamp = System.currentTimeMillis() / 1000;
            if ("SUCCESS".equals(return_code) && return_code.equals(result_code))
            {
                String prepayid = rMap.get("prepay_id");
                resultMap.put("package", "prepay_id=" + prepayid);
                resultMap.put("signType", "MD5");
                //这边要将返回的时间戳转化成字符串，不然小程序端调用wx.requestPayment方法会报签名错误
                resultMap.put("timeStamp", timeStamp + "");
                //再次签名，这个签名用于小程序端调用wx.requesetPayment方法
                resultMap.put("appId", config.getAppID());
                String sign = WXPayUtil.generateSignature(resultMap, config.getKey());
                resultMap.put("paySign", sign);
                LOGGER.info("生成的签名paySign-->[{}]", sign);
                // 生成预定单信息 自己的业务
                // ...
                return ResultData.success(resultMap);
            } else
            {
                return ResultData.fail("333", "支付失败");
            }
        } catch (Exception e)
        {
            e.printStackTrace();
            return ResultData.fail("332", "统一下单接口返回失败");
        }
    }

    @Override
    public void wxAppletPayCallback(HttpServletRequest request, HttpServletResponse response)
    {
        String resultCode = null;
        String resultMsg = null;
        //1 获取微信支付异步回调结果
        String xmlResult = WeiXinPayUtil.getPostStr(request);
        LOGGER.error("微信回调信息-->[{}]", xmlResult);
        Map<String, String> resultMap = null;
        try
        {
            //将结果转成map
            resultMap = WXPayUtil.xmlToMap(xmlResult);
        } catch (Exception e1)
        {
            e1.printStackTrace();
            LOGGER.error("回调信息转化成map异常!");
        }
        //订单号
        String orderNo = resultMap.get("out_trade_no");
        String result_code = resultMap.get("result_code");
        if (result_code.equals("SUCCESS"))
        {
            //回调返回的加密签名 保存下来 下面会进行对比
            String sign = resultMap.get("sign");
            //去掉sign和利用微信回调回来的信息重新加密
            resultMap.remove("sign");
            String sign1 = "";
            try
            {
                //重新加密 获取加密的签名
                sign1 = WXPayUtil.generateSignature(resultMap, new WXConfig().getKey()); //签名
            } catch (Exception e)
            {
                e.printStackTrace();
                LOGGER.error("重新加密异常!");
            }
            //对比微信回调的加密与重新加密是否一致  一致即为通过 不一致说明呗改动过 加密不通过
            if (sign.equals(sign1))
            { //验签通过
                // 更新用户金币以及更新订单信息 根据自己的业务
                // ...
                resultCode = "SUCCESS";
                resultMsg = "成功";
            } else
            {
                resultCode = "FAIL";
                resultMsg = "验签未通过";
                // ...//自己的业务
            }
        } else
        {
            resultCode = "FAIL";
            resultMsg = "验签未通过";
            // ...//自己的业务
        }
        Map<String, String> returnMap = new HashMap<>();
        returnMap.put("return_code", resultCode);
        returnMap.put("return_msg", resultMsg);
        try
        {
            String resultStr = WXPayUtil.mapToXml(returnMap);
            response.getWriter().write(resultStr);
        } catch (Exception e)
        {
            e.printStackTrace();
            LOGGER.error("map-to-xml异常!");
        }
    }
}
